package be.pluyckx.batteryprogress.activity.fragment.info.history;

import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import be.pluyckx.batteryprogress.R;
import be.pluyckx.batteryprogress.application.Constants;
import be.pluyckx.batteryprogress.components.view.Chart;
import be.pluyckx.batteryprogress.data.Observer;
import be.pluyckx.batteryprogress.data.history.BatteryHistory;
import be.pluyckx.batteryprogress.data.history.BatteryLevel;
import be.pluyckx.batteryprogress.utils.DateTime;

/**
 * Created by Philip on 21/08/13.
 * <p/>
 * A Fragment that holds a Chart view. This can also be used as a DialogFragment.
 */
public class ChartFragment extends DialogFragment {
  /**
   * A Chart view
   */
  private Chart mChart;
  /**
   * The data used by the chart
   */
  private ChartData mChartData = null;

  @Override
  public void onResume() {
    super.onResume();

    mChartData = new ChartData();
    mChart.setData(mChartData);
  }

  @Override
  public void onPause() {
    super.onPause();

    mChart.setData(null);
    mChartData.destroy();
    mChartData = null;
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_chart, container, false);

    if (getShowsDialog()) {
      getDialog().setTitle(R.string.dialog_graph);
    }

    mChart = (Chart) v.findViewById(R.id.chart_view);
    mChart.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        if (!getShowsDialog()) {
          DialogFragment dialog = new ChartFragment();
          dialog.show(getActivity().getSupportFragmentManager(), "chart");
        } else {
          dismiss();
        }
      }
    });

    return v;
  }

  /**
   * A private class used by Chart.
   */
  private static class DataItem implements Chart.DataItem {
    private long mX;
    private float mY;
    private String msX;
    private String msY;
    private int mState;

    @Override
    public int getState() {
      return mState;
    }

    void setState(int state) {
      mState = state;
    }

    @Override
    public float getX() {
      return mX;
    }

    @Override
    public float getY() {
      return mY;
    }

    public void setX(long x) {
      if (mX != x) {
        mX = x;
        msX = DateTime.toTimeString(mX);
      }
    }

    @Override
    public void setX(float x) {
      setX((long) x);
    }

    @Override
    public void setY(float y) {
      if (mY != y) {
        mY = y;
        msY = Constants.percentFormat.format(y);
      }
    }

    @Override
    public String getXAsString() {
      return msX;
    }

    @Override
    public String getYAsString() {
      return msY;
    }
  }

  /**
   * A wrapper class around the BatteryHistory. This class will convert some fields.
   */
  private static class ChartData extends Chart.DataCollection implements Observer {
    private BatteryHistory mHistory = BatteryHistory.getHistory();
    private long mMaxX = 0;
    private float mMaxY = 0.0f;
    private String msMaxX = "0";
    private String msMaxY = "0";

    /**
     * After usage, call {@code destroy}
     */
    public ChartData() {
      mHistory.addObserver(this);
    }

    /**
     * Frees all used resources.
     */
    public void destroy() {
      mHistory.removeObserver(this);
      mHistory = null;
    }

    @Override
    public int size() {
      return mHistory.size();
    }

    @Override
    public Chart.DataItem get(int index, Chart.DataItem item) {
      if (item == null) {
        item = createItem();
      } else if (!(item instanceof DataItem)) {
        throw new IllegalArgumentException("The item isn't an instance of DataItem from ChartFragment!");
      }

      DataItem di = (DataItem) item;

      BatteryLevel level = mHistory.get(index);
      di.setX(level.getElapsed() - mHistory.getFirst().getElapsed());
      di.setY(level.getBatteryLevelPercentage());
      di.setState(level.getState());

      return di;
    }

    @Override
    public Chart.DataItem getFirst(Chart.DataItem item) {
      return get(0, item);
    }

    @Override
    public Chart.DataItem getLast(Chart.DataItem item) {
      return get(size() - 1, item);
    }

    @Override
    public int getOrdered() {
      return ORDER_OLD_LOWEST_INDEX;
    }

    @Override
    public float getMaxY() {
      return mMaxY;
    }

    @Override
    public float getMaxX() {
      return mMaxX;
    }

    @Override
    public String getMaxYAsString() {
      return msMaxY;
    }

    @Override
    public String getMaxXAsString() {
      return msMaxX;
    }

    public DataItem createItem() {
      return new DataItem();
    }

    /**
     * Helper method to calculate the maximum Y value in the history.
     */
    private void calculateMaxY() {
      float maxY = 0.0f;
      if (mHistory.size() > 0) {
        maxY = mHistory.get(0).getBatteryLevelPercentage();

        for (int i = 0; i < mHistory.size(); i++) {
          BatteryLevel l = mHistory.get(i);
          if (l.getBatteryLevelPercentage() > maxY) {
            maxY = l.getBatteryLevelPercentage();
          }
        }
      }

      mMaxY = maxY;
      msMaxY = Constants.percentFormat.format(mMaxY);
    }

    /**
     * Helper method to calculate the maximum X value in the history.
     */
    private void calculateMaxX() {
      if (mHistory.size() > 0) {
        BatteryLevel lowestIndex = mHistory.get(0);
        BatteryLevel highestIndex = mHistory.get(mHistory.size() - 1);

        if (getOrdered() == ORDER_OLD_LOWEST_INDEX) {
          mMaxX = highestIndex.getElapsed() - lowestIndex.getElapsed();
        } else {
          mMaxX = lowestIndex.getElapsed() - highestIndex.getElapsed();
        }
      } else {
        mMaxX = 0;
      }

      msMaxX = DateTime.toTimeString(mMaxX);
    }

    @Override
    public void onDataChanged(Object observable, ChangeInfo info) {
      calculateMaxX();
      calculateMaxY();

      fireDataChanged();
    }
  }
}
